package stateDiagram;

import java.awt.Color;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.net.URI;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

import static org.jhotdraw.draw.AttributeKeys.TEXT_COLOR;

import org.jhotdraw.draw.Drawing;
import org.jhotdraw.draw.Figure;
import org.jhotdraw.draw.TextFigure;

import stateDiagram.EntityObjects.StateEntity;
import stateDiagram.EntityObjects.SubStateEntity;
import stateDiagram.EntityObjects.SuperStateEntity;
import stateDiagram.EntityObjects.TransitionEntity;
import stateDiagram.figures.EndStateFigure;
import stateDiagram.figures.StartStateFigure;
import stateDiagram.figures.StateFigure;

public class AnimationEngine {
	
	//A basic overview of how this will work:
	/* The AnimationEngine will hold pointers to the current state or transition 
	 * it's currently at. When animate() is called, it will go onto the next line
	 * in the given instruction set and transition from there if it's on a state, or
	 * if it's on a transition it will continue onto the next state. However, the 
	 * animate() will only work as described if the time since the last successful
	 * call is greater than or equal to the set interval time, given as the first line
	 * in the instruction set as seconds.
	 * 
	 * If the current state is a super or substate, then we will hold a temporary copy
	 * of the superstate in case we get a call to continue along a transition of the
	 * superstate when we're in a substate.
	 */
	
	//What are our current states/transitions?
	private StateEntity currentState;
	private TransitionEntity currentTransition;
	private boolean isOnState; //true, we're on state. false, we're on transition.
	
	//Holder for our current superstate
	private SuperStateEntity currentSuperState;
	private boolean isUnderSuperState; //true, we're in a substate
	
	//Our instructions
	Queue<String> instructions;
	
	//Variables for timing
	private long afterExecutionTime; 
	private long beforeExecutionTime;
	private long duration = 0;
	private final long interval = 2000;
	
	//Variable for closing out animate
	private Queue<TextFigure> closingList;
	
	//This is indeed a boolean. It should always return true. When the instructions run
	//out and no more can be animated, then it returns false. This is needed for the
	//worker in the animateDrawing action.
	public boolean animate(){		
		beforeExecutionTime = System.currentTimeMillis();
		duration = beforeExecutionTime - afterExecutionTime;
		
		boolean toContinue = true;
		
		if (duration >= interval){
			//Reset duration to 0
			duration = 0;
			
			//Animate
			
			/*
			if (currentState == null && currentTransition == null){
				//Start up the whole process by making the start state our current 
				//state.
				
				List<Figure> allFigures = drawing.getChildren();
				
				Iterator<Figure> it = allFigures.iterator();
				while (it.hasNext()){
					Figure f = it.next();
					if (f != null){
						if (f instanceof StartStateFigure)
							currentState = ((StartStateFigure) f).getStateEntity();
					}
				}
				
			}
			else */
			/*
			 * The preceding part has been removed because our constructor should set
			 * current state to the start state.
			 */
			
			if (isOnState){
				//If we're on a state, we go through the next instruction and see if we
				//have a transition that matches our instruction. If we do, we make that
				//our current transition and animate it.
				
				//However, if we're under a superstate, we should check if the transition
				//leading out of the superstate has been triggered or not.
				
				if (!instructions.isEmpty() && !currentState.getName().equals("end") ){
					
					String instr = instructions.poll();
					
					if (isUnderSuperState){
						List<TransitionEntity> l = currentSuperState.getOutgoingTransitions();
						Iterator<TransitionEntity> it = l.iterator();
						while (it.hasNext()){
							TransitionEntity holder = it.next();
							if (!holder.isInternalTransition())
								if (holder.getName().equals(instr)){
									currentTransition = holder;
									isUnderSuperState = false;
									isOnState = false;
								}
						}
					}
					
					if (isOnState){
						List<TransitionEntity> l = currentState.getOutgoingTransitions();
						Iterator<TransitionEntity> it = l.iterator();
						while (it.hasNext()){
							TransitionEntity holder = it.next();
							if (holder.getName().equals(instr)){
								currentTransition = holder;
								isOnState = false;
								
							}
						}
					}
					
					if (currentTransition != null){
						
						
						
						if (!currentState.getName().equals("startState") && 
								!currentState.getName().equals("end")){
							currentState.getFigure().willChange();
							currentState.getFigure().getExitFigure().set(TEXT_COLOR, textColorSet(currentState.getFigure().getExitFigure()));
							currentState.getFigure().changed();
							closingList.add(currentState.getFigure().getExitFigure());
						}
						
						currentTransition.getFigure().willChange();
						currentTransition.getFigure().getActionNameFigure().set(TEXT_COLOR, textColorSet(currentTransition.getFigure().getActionNameFigure()));
						closingList.add(currentTransition.getFigure().getActionNameFigure());
						currentTransition.getFigure().changed();
					}
					
				} else {
					//The animation is complete.
					toContinue = false;
				}
				
			} else {
				//We are currently on a transition and should move to the next state.
				//Then we check and flag certain variables depending on what that state
				//is.
				
				currentState = currentTransition.getNextState();
				
				//animate currentstate
				if (!currentState.getName().equals("end")){
					currentState.getFigure().willChange();
					currentState.getFigure().getNameFigure().set(TEXT_COLOR, textColorSet(currentState.getFigure().getNameFigure()));
					currentState.getFigure().getEntryFigure().set(TEXT_COLOR, textColorSet(currentState.getFigure().getEntryFigure()));
					currentState.getFigure().changed();
					closingList.add(currentState.getFigure().getNameFigure());
					closingList.add(currentState.getFigure().getEntryFigure());
				}
				
				if (currentState instanceof SuperStateEntity){
					currentSuperState = (SuperStateEntity) currentState;
					isUnderSuperState = true;
				} else if (currentState instanceof SubStateEntity){
					isUnderSuperState = true;
				} else {
					isUnderSuperState = false;
				}
				
				isOnState = true;
				
			}
		
			afterExecutionTime = System.currentTimeMillis();
		}
		
		return toContinue;
	}
	
	//This undoes what all our animation did.
	public void end(){
		//HOPE WE AREN'T USING CUSTOM TEXT COLORS.
		
		while (!closingList.isEmpty()){
			TextFigure holder = closingList.poll();
			holder.willChange();
			holder.set(TEXT_COLOR, Color.BLACK);
			holder.changed();
		}
	}
	
	//Still needs constructer that fills up the queue and sets the starting state!
	public AnimationEngine(URI uri, Drawing drawing){
		//initialize all variables.
		currentState = null;
		currentTransition = null;
		isOnState = false;
		currentSuperState = null;
		isUnderSuperState = false;
		instructions = new LinkedList<String>();
		closingList = new LinkedList<TextFigure>();
		duration = 0;
		
		
		//Set up instruction queue.
		try {
			
			FileReader fr = new FileReader(new File(uri));
			BufferedReader br = new BufferedReader(fr);
			
			String s;
			
			while ((s = br.readLine()) != null){
				s = s.trim();
				instructions.add(s);
			}
			
			br.close();
			fr.close();
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		//Get start state.
		
		List<Figure> l = drawing.getChildren();
		Iterator<Figure> it = l.iterator();
		
		while (it.hasNext() && currentState == null){
			Figure f = it.next();
			if (f instanceof StartStateFigure){
				currentState = ((StartStateFigure) f).getStateEntity();
				isOnState = true;
			}
		}
		
		//Set up start time.
		afterExecutionTime = System.currentTimeMillis();
	}
	
	public Color textColorSet(TextFigure target){
		Color whatsthere = target.getTextColor();
		if (whatsthere == Color.BLACK){
			return Color.GREEN;
		} else if (whatsthere == Color.GREEN){
			return Color.YELLOW;
		} else if (whatsthere == Color.YELLOW){
			return Color.DARK_GRAY;
		} else if (whatsthere == Color.DARK_GRAY){
			return Color.BLACK;
		}
		return Color.BLACK;
	}
	
	
}
